package com.itsxb.service.impl;

import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.itsxb.constants.Constants;
import com.itsxb.constants.GlobalExceptionCode;
import com.itsxb.mapper.PermissionMapper;
import com.itsxb.pojo.domain.Login;
import com.itsxb.mapper.LoginMapper;
import com.itsxb.pojo.dto.AdminLoginDto;
import com.itsxb.pojo.dto.LoginDto;
import com.itsxb.pojo.vo.TokenVo;
import com.itsxb.service.ILoginService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itsxb.template.WechatTemplate;
import com.itsxb.utils.AssertUtil;
import com.itsxb.utils.NameUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;

/**
 * <p>
 * 登录表 服务实现类
 * </p>
 *
 * @author 奇天书
 * @since 2023-12-22
 */
@Service
public class LoginServiceImpl extends ServiceImpl<LoginMapper, Login> implements ILoginService {
    @Autowired
    private WechatTemplate wechatTemplate;

    //这里不需要自动装配
    @Autowired
    private PermissionMapper permissionMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${fccar.wechat.default-avatar}")
    private String dafaultAvatar;

    @Value("${sa-token.permission-template}")
    private String permissionTemplate;

    /**
     * 司机和客户注册的时候调用该接口 初始化login信息
     * @param loginDto 他是什么时候被赋值的
     */
    @Override
    public Long saveLogin(LoginDto loginDto) {
        Login login = new Login();
        login.setId(loginDto.getId());
        login.setType(loginDto.getType());
        login.setEnabled(true);
        login.setAvatar(dafaultAvatar);
        login.setAdmin(false);
        login.setNickName(NameUtil.getName());
        login.setOpenId(loginDto.getOpenId());
        login.setPhone(loginDto.getPhone());
        login.setCreateTime(new Date());
        super.save(login);
        return login.getId();
    }

    @Override
    public TokenVo wechatLogin(String loginCode) {
        //JSR303
        //通过前端的longinCode获取openId
        String coded2OpenId = wechatTemplate.code2OpenId(loginCode);
        //不是空 继续执行，是空报异常
        AssertUtil.isNotEmpty(coded2OpenId, GlobalExceptionCode.PARAM_CODE_ERROR);
        //业务校验
        //数据库中根据openId查询
        Login login = super.getOne(new LambdaQueryWrapper<Login>().eq(Login::getOpenId, coded2OpenId));

        //只要当login不是空的时候 才会往下执行
        AssertUtil.isNotNull(login, GlobalExceptionCode.WECHAT_REGISTER_ERROR);
        //3.0 业务逻辑实现
        //3.0.1
        return executeAdminLogin(login);
    }

    @Override
    public TokenVo login(AdminLoginDto adminLoginDto) {
        //JSR303检验 校验前端传过来的数据 是否是空
        //业务校验
        //3.业务实现
        //3.1 去数据库查询账号是否存在
        Login login = super.getOne(new LambdaQueryWrapper<Login>().eq(Login::getUsername, adminLoginDto.getUsername()));
        AssertUtil.isNotNull(login.getUsername(),GlobalExceptionCode.PARAM_ERROR);
        //3.2 去数据库查询密码是否存在 需要在这里去添加BCrypt算法
//        AssertUtil.isEquals(login.getPassword(),adminLoginDto.getPassword(),GlobalExceptionCode.PARAM_ERROR);
        String hashpw = BCrypt.hashpw(login.getPassword());
        boolean checkpw = BCrypt.checkpw(login.getPassword(), hashpw);
        AssertUtil.isTrue(checkpw,GlobalExceptionCode.PASSWORD_ERROR);
        //使用Ctrl+Alt+M抽离代码
        return executeAdminLogin(login);

    }

    @Override
    public void logout() {
        //因为是退出登录 所以不需要前端传输参数给后端 不需要JSR303
        //业务校验 也没有
        //3.业务实现
        //3.1从Redis中删除权限码
        //3.1.1 拿到当前用户的id
        long loginIdAsLong = StpUtil.getLoginIdAsLong();
        //3.1.2 格式化permission键
        String format = String.format(permissionTemplate,loginIdAsLong);
        //3.1.3 按照permission键删除值
        redisTemplate.delete(format);
        //3.2 使用Satoken退出登录
        StpUtil.logout();
    }

    private TokenVo executeAdminLogin(Login login) {
        //3.3 用satoken登录，生成tokenName和tokenValue
        StpUtil.login(login.getId());
        String tokenName = StpUtil.getTokenName();
        String tokenValue = StpUtil.getTokenValue();
        TokenVo tokenVo = new TokenVo();
        //3.4存储到Redis中
        if(login.getType().equals(Constants.Login.TYPE_ADMIN)){
            //将application中的permissionTemplate取出来 拼接成唯一的permission的键
            String format = String.format(permissionTemplate, login.getId());
            List<String> selectPermissionCode = permissionMapper.selectPermissionCode(login.getId());
            tokenVo.setPermissionCodes(selectPermissionCode);
            redisTemplate.opsForValue().set(format,selectPermissionCode);
        }

        //在传输给前端时不需要密码
        login.setPassword("");
        tokenVo.setTokenName(tokenName)
                .setTokenValue(tokenValue)
                .setLoginInfo(login);
        return tokenVo;
    }
}
